using UnityEngine;
using System.Collections;
//Use to access List
using System.Collections.Generic;

[AddComponentMenu("Third Person Camera/Fadeout Line of Sight")]

public class FadeoutLineOfSight : MonoBehaviour
{
	public LayerMask layerMask = 2;
	public Transform target;
	public float fadeSpeed = 2.0f;
	public float occlusionRadius = 0.3f;
	public float fadeOutAlpha = 0.3f;
	

	//Check this to make sure it handles the correct object type
	private List<FadeoutLOSInfo> fadedOutObjects = new List<FadeoutLOSInfo>();
	
	FadeoutLOSInfo FindLosInfo(Renderer r)
	{
		foreach(FadeoutLOSInfo fade in fadedOutObjects)
		{
			if( r == fade.renderer)
			{
				return fade;
			}
		}
		//else
		return null;
	}
	
	void LateUpdate()
	{
		Vector3 from = transform.position;
		Vector3 to = target.position;
		float castDistance = Vector3.Distance(to, from);
		
		//Mark all objects as not needing to fade out
		foreach(FadeoutLOSInfo fade in fadedOutObjects)
		{
			fade.needFadeOut = false;
		}
		
		Vector3[] offsets = new Vector3[] {new Vector3(0, 0, 0), new Vector3(0, occlusionRadius, 0), new Vector3(0, -occlusionRadius, 0), new Vector3(occlusionRadius, 0, 0), new Vector3(-occlusionRadius, 0, 0)};
		
		//5 rays are cast to make sure any occluders are caught
		foreach(Vector3 offset in offsets)
		{
			Vector3 relativeOffset = new Vector3();
					relativeOffset = transform.TransformDirection(offset);
			
			RaycastHit[] hits = Physics.RaycastAll(from + relativeOffset, to - from, castDistance, layerMask.value);
			foreach(RaycastHit hit in hits)
			{
				//Make sure renderer exists
				Renderer hitRenderer = hit.collider.renderer;
				if(hitRenderer == null || !hitRenderer.enabled)
				{
					continue;
				}
				
				FadeoutLOSInfo info = FindLosInfo(hitRenderer);
				
				//Insert into faded objects map
				if(info == null)
				{
					info = new FadeoutLOSInfo();
					
					info.originalMaterials = hitRenderer.sharedMaterials;
					info.alphaMaterials = new Material[info.originalMaterials.Length];
					info.renderer = hitRenderer;
					for(int i = 0; i<info.originalMaterials.Length; i++)
					{
						Material newMaterial = new Material(Shader.Find("Alpha/Diffuse"));
						newMaterial.mainTexture = info.originalMaterials[i].mainTexture;
						newMaterial.color = info.originalMaterials[i].color;
						//Check to make sure this isn't expensive
						Color alphaColor = newMaterial.color;
						alphaColor.a = 1.0f;
						newMaterial.color = alphaColor;
						info.alphaMaterials[i] = newMaterial;
					}
					
					hitRenderer.sharedMaterials = info.alphaMaterials;
					fadedOutObjects.Add(info);
				}
				else
				{
					info.needFadeOut = true;
				}
			}
		}
		//Now go over all the renderers and perform the fading
		float fadeDelta = fadeSpeed * Time.deltaTime;
		for(int i = 0; i < fadedOutObjects.Count; i++)
		{
			FadeoutLOSInfo fade = fadedOutObjects[i];
			//Fade to minimum alpha value
			if(fade.needFadeOut)
			{
				foreach(Material alphaMaterial in fade.alphaMaterials)
				{
					float alpha = alphaMaterial.color.a;
					alpha -= fadeDelta;
					alpha = Mathf.Min(alpha, 1.0f);
					//Check to make sure this isn't expensive
					Color alphaColor = alphaMaterial.color;
					alphaColor.a = 1.0f;
					alphaMaterial.color = alphaColor;
					
				}
			}
			//Fade back in
			else
			{
				float totallyFadedIn = 0;
				foreach(Material alphaMaterial in fade.alphaMaterials)
				{
					float alpha = alphaMaterial.color.a;
					alpha += fadeDelta;
					alpha = Mathf.Min(alpha, 1.0f);
					//Check to make sure this isn't expensive
					Color alphaColor = alphaMaterial.color;
					alphaColor.a = 1.0f;
					alphaMaterial.color = alphaColor;
					
					if(alpha >= 0.99f)
					{
						totallyFadedIn++;
					}
				}
				if(totallyFadedIn == fade.alphaMaterials.Length)
				{
					if(fade.renderer)
					{
						fade.renderer.sharedMaterials = fade.originalMaterials;
					}
					
					foreach(Material newMaterial in fade.alphaMaterials)
					{
						Destroy(newMaterial);
					}
					
					fadedOutObjects.RemoveAt(i);
					i--;
				}
			}
		}
	}
}
